home *** CD-ROM | disk | FTP | other *** search
- version equ 3
- page ,132
- include defs.asm
-
- ;/* PC/FTP Packet Driver source, conforming to version 1.05 of the spec,
- ;* for the NE2000 interface card.
- ;* Robert C Clements, K1BC, 14 February, 1989
- ;* Portions (C) Copyright 1988, 1989 Robert C Clements
- ;* Modified from 3com503 driver by D.J.Horne
- ;*
- ; Copyright, 1988, 1989, Russell Nelson
-
- ; This program is free software; you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, version 1.
- ;
- ; This program is distributed in the hope that it will be useful,
- ; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ; GNU General Public License for more details.
- ;
- ; You should have received a copy of the GNU General Public License
- ; along with this program; if not, write to the Free Software
- ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- ;* Change history:
- ;* Updated to driver spec version 1.08 Feb. 17, 1989 by Russell Nelson.
- ;* Changes 27 Jul 89 by Bob Clements (/Rcc)
- ;* Added Thick versus Thin Ethernet switch 27 Jul 89 by Bob Clements (/Rcc)
- ;* Added call to memory_test.
- ;* Added rcv_mode logic. Started, but didn't finish, multicast logic.
- ;* Fixed get_address to return current, not PROM, address.
- ;* Minor races fixed.
- ;* Changes 19 Oct 89, Dave Horne
- ;* Modified for NE2000, use i/o instead of shared memory,
- ;* remove thick/thin logic, remove gate array logic
-
- code segment word public
- assume cs:code, ds:code
-
- ; Stuff specific to the NE2000 Ethernet controller board
- ; WD version in C by Bob Clements, K1BC, May 1988 for the KA9Q TCP/IP package
- ; 3Com version based on WD8003E version in .ASM, also by Bob Clements, dated
- ; 19 August 1988. The WD and 3Com cards both use the National DS8390.
- ; NE2000 based on 3COM503 version.
-
- ; Symbol prefix "EN" is for Ethernet, National chip
- ; Symbol prefix "NE" is for NE2000 register(s)
-
- ; The EN registers - the DS8390 chip registers
- ; These appear at Base+0 through Base+0F
- ; There are two (really 3) pages of registers in the chip. You select
- ; which page you want, then address them at offsets 00-0F from base.
- ; The chip command register (EN_CCMD) appears in both pages.
-
- EN_CCMD equ 000h ; Chip's command register
-
- ; Page 0
-
- EN0_STARTPG equ 001h ; Starting page of ring bfr
- EN0_STOPPG equ 002h ; Ending page +1 of ring bfr
- EN0_BOUNDARY equ 003h ; Boundary page of ring bfr
- EN0_TSR equ 004h ; Transmit status reg
- EN0_TPSR equ 004h ; Transmit starting page
- EN0_TCNTLO equ 005h ; Low byte of tx byte count
- EN0_TCNTHI equ 006h ; High byte of tx byte count
- EN0_ISR equ 007h ; Interrupt status reg
- EN0_RSARLO equ 008h ; Remote start address reg 0
- EN0_RSARHI equ 009h ; Remote start address reg 1
- EN0_RCNTLO equ 00ah ; Remote byte count reg
- EN0_RCNTHI equ 00bh ; Remote byte count reg
- EN0_RXCR equ 00ch ; RX control reg
- EN0_TXCR equ 00dh ; TX control reg
- EN0_COUNTER0 equ 00dh ; Rcv alignment error counter
- EN0_DCFG equ 00eh ; Data configuration reg
- EN0_COUNTER1 equ 00eh ; Rcv CRC error counter
- EN0_IMR equ 00fh ; Interrupt mask reg
- EN0_COUNTER2 equ 00fh ; Rcv missed frame error counter
-
- ; Page 1
-
- EN1_PHYS equ 001h ; This board's physical enet addr
- EN1_CURPAG equ 007h ; Current memory page
- EN1_MULT equ 008h ; Multicast filter mask array (8 bytes)
-
- ; Board regs
-
- NE_DATAPORT equ 10h
- NE_OTHERPORT equ 1fh
-
- ; Chip commands in EN_CCMD
- ENC_STOP equ 001h ; Stop the chip
- ENC_START equ 002h ; Start the chip
- ENC_TRANS equ 004h ; Transmit a frame
- ENC_RREAD equ 008h ; remote read
- ENC_RWRITE equ 010h ; remote write
- ENC_NODMA equ 020h ; No remote DMA used on this card
- ENC_PAGE0 equ 000h ; Select page 0 of chip registers
- ENC_PAGE1 equ 040h ; Select page 1 of chip registers
-
- ; Commands for RX control reg
- ENRXCR_MON equ 020h ; Monitor mode (no packets rcvd)
- ENRXCR_PROMP equ 010h ; Promiscuous physical addresses
- ENRXCR_MULTI equ 008h ; Multicast (if pass filter)
- ENRXCR_BCST equ 004h ; Accept broadcasts
- ENRXCR_BAD equ 003h ; Accept runts and bad CRC frames
-
- ; Commands for TX control reg
- ENTXCR_LOOP equ 002h ; Set loopback mode
-
- ; Bits in EN0_DCFG - Data config register
- ENDCFG_BM8 equ 049h ; Set burst mode, 8 deep FIFO, words
-
- ; Bits in EN0_ISR - Interrupt status register
- ENISR_RX equ 001h ; Receiver, no error
- ENISR_TX equ 002h ; Transmitter, no error
- ENISR_RX_ERR equ 004h ; Receiver, with error
- ENISR_TX_ERR equ 008h ; Transmitter, with error
- ENISR_OVER equ 010h ; Receiver overwrote the ring
- ENISR_COUNTERS equ 020h ; Counters need emptying
- ENISR_RDC equ 040h ; remote dma complete
- ENISR_RESET equ 080h ; Reset completed
- ENISR_ALL equ 03fh ; Interrupts we will enable
-
- ; Bits in received packet status byte and EN0_RSR
- ENPS_RXOK equ 001h ; Received a good packet
-
- ; Bits in TX status reg
-
- ENTSR_PTX equ 001h ; Packet transmitted without error
- ENTSR_COLL equ 004h ; Collided at least once
- ENTSR_COLL16 equ 008h ; Collided 16 times and was dropped
- ENTSR_FU equ 020h ; TX FIFO Underrun
-
- ; Shared memory management parameters
-
- XMIT_MTU equ 600h ; Largest packet we have room for.
- SM_TSTART_PG equ 040h ; First page of TX buffer
- SM_RSTART_PG equ 046h ; Starting page of RX ring
- SM_RSTOP_PG equ 080h ; Last page +1 of RX ring
-
- ; Description of header of each packet in receive area of memory
-
- EN_RBUF_STAT equ 0 ; Received frame status
- EN_RBUF_NXT_PG equ 1 ; Page after this frame
- EN_RBUF_SIZE_LO equ 2 ; Length of this frame
- EN_RBUF_SIZE_HI equ 3 ; Length of this frame
- EN_RBUF_NHDR equ 4 ; Length of above header area
-
- ; End of NE2000 parameter definitions
-
- pause_ macro
- jmp $+2
- endm
-
- longpause macro
- push cx
- mov cx,0
- loop $
- pop cx
- endm
-
- ; The following two values may be overridden from the command line.
- ; If they are omitted from the command line, these defaults are used.
- ; The shared memory base is set by a jumper. We read it from the
- ; card and set up accordingly.
-
- public int_no, io_addr
- int_no db 2,0,0,0 ; Interrupt level
- io_addr dw 0300h,0 ; I/O address for card (jumpers)
-
- public driver_class, driver_type, driver_name, driver_function, parameter_list
- driver_class db 1 ;from the packet spec
- driver_type db 54 ;from the packet spec
- driver_name db 'NE2000',0 ;name of the driver.
- driver_function db 2
- parameter_list label byte
- db 1 ;major rev of packet driver
- db 9 ;minor rev of packet driver
- db 14 ;length of parameter list
- db EADDR_LEN ;length of MAC-layer address
- dw GIANT ;MTU, including MAC headers
- dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
- dw 0 ;(# of back-to-back MTU rcvs) - 1
- dw 0 ;(# of successive xmits) - 1
- int_num dw 0 ;Interrupt # to hook for post-EOI
- ;processing, 0 == none,
-
- rxcr_bits db ENRXCR_BCST ; Default to ours plus multicast
-
-
- public card_hw_addr, curr_hw_addr, mcast_list_bits, mcast_all_flag
- card_hw_addr db 0,0,0,0,0,0 ;Physical ethernet address
- curr_hw_addr db 0,0,0,0,0,0 ;Address set into the 8390
- mcast_list_bits db 0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
- mcast_all_flag db 0 ;Non-zero if hware should have all
- ; ones in mask rather than this list.
- mcast_sw_filter db 0 ; set if software filter is required.
- is_186 db 0
- mcast_sw_fin dw 0
- mcast_sw_fout dw 0
-
- public rcv_modes
- rcv_modes dw 7 ;number of receive modes in our table.
- dw 0 ;There is no mode zero
- dw rcv_mode_1
- dw rcv_mode_2
- dw rcv_mode_3
- dw rcv_mode_4
- dw rcv_mode_5
- dw rcv_mode_6
-
- public mcast_tab
- mcast_hcount dw 0 ; multicast header count
- mcast_tab_b db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh ; entry for broadcast
- mcast_tab db (MAX_MULTICAST*EADDR_LEN) dup (0)
- ;
- ; a temp buffer for the received header
- ;
- RCV_HDR_SIZE equ 18 ; 2 ids @6 + protocol, + 4byte header
- rcv_hdr db RCV_HDR_SIZE dup(0)
-
- ;
- ; The board data
- ;
- public board_data
- BOARD_DATA_SIZE equ 32
- board_data db BOARD_DATA_SIZE dup(0)
- soft_tx_errors dw 0,0
- soft_tx_err_bits db 0
- soft_rx_errors dw 0,0
- soft_rx_err_bits db 0
-
-
-
- ; send_pkt: - The Transmit Frame routine
-
- public as_send_pkt
- ; The Asynchronous Transmit Packet routine.
- ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
- ; interrupts possibly enabled.
- ; Exit with nc if ok, or else cy if error, dh set to error number.
- ; es:di and interrupt enable flag preserved on exit.
- as_send_pkt:
- ret
-
- public drop_pkt
- ; Drop a packet from the queue.
- ; Enter with es:di -> iocb.
- drop_pkt:
- assume ds:nothing
- ret
-
- public xmit
- ; Process a transmit interrupt with the least possible latency to achieve
- ; back-to-back packet transmissions.
- ; May only use ax and dx.
- xmit:
- assume ds:nothing
- ret
-
-
- public send_pkt
- send_pkt:
- ;enter with ds:si -> packet, cx = packet length.
- ;exit with nc if ok, or else cy if error, dh set to error number.
- assume ds:nothing
- loadport ; Point at chip command register
- setport EN_CCMD ; ..
- pause_
- mov bx, 8000h ; Avoid infinite loop
- tx_wait:
- in al, dx ; Get chip command state
- test al,ENC_TRANS ; Is transmitter still running?
- jz tx_idle ; Go if free
- dec bx ; Count the timeout
- jnz tx_wait ; Fall thru if TX is stuck
- call count_out_err ; Should count these error timeouts
- ; Maybe need to add recovery logic here
- tx_idle:
- cmp cx,XMIT_MTU ; Is this packet too large?
- ja send_pkt_toobig
-
- cmp cx, RUNT ; Is the frame long enough?
- jnb tx_oklen ; Go if OK
- mov cx, RUNT ; Stretch frame to minimum allowed
- tx_oklen:
- push cx ; Hold count for later
- loadport ; Set up for address
- setport EN0_ISR
- pause_
- mov al,ENISR_RDC ; clear remote interrupt int.
- out dx,al
- setport EN0_TCNTLO ; Low byte of TX count
- pause_
- mov al, cl ; Get the count
- out dx, al ; Tell card the count
- setport EN0_TCNTHI ; High byte of TX count
- pause_
- mov al, ch ; Get the count
- out dx, al ; Tell card the count
- xor ax, ax ; Set up ax at base of tx buffer
- mov ah, SM_TSTART_PG ; Where to put tx frame
- pop cx ; Get back count to give to board
- call block_output
- loadport
- mov cx,0
- setport EN0_ISR
- in al,dx
- tx_check_rdc:
- test al,ENISR_RDC ; dma done ???
- jnz tx_start
- loop tx_check_rdc
- jmp tx_no_rdc
- tx_start:
- setport EN0_TPSR ; Transmit Page Start Register
- pause_
- mov al, SM_TSTART_PG
- out dx, al ; Start the transmitter
- setport EN_CCMD ; Chip command reg
- pause_
- mov al, ENC_TRANS+ENC_NODMA+ENC_START
- out dx, al ; Start the transmitter
- clc ; Successfully started
- sti
- ret ; End of transmit-start routine
- send_pkt_toobig:
- mov dh,NO_SPACE
- stc
- sti
- ret
- tx_no_rdc:
- mov dh,CANT_SEND
- stc
- sti
- ret
-
- count_soft_err:
- add word ptr soft_tx_errors,1
- adc word ptr soft_tx_errors+2,0
- or byte ptr soft_tx_err_bits,al
- ret
-
- include movemem.asm
-
-
- public get_address
- get_address:
- ;get the address of the interface.
- ;enter with es:di -> place to get the address, cx = size of address buffer.
- ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
- ; Give caller the one currently in the 8390, not necessarily the one in PROM.
- assume ds:code
- cmp cx, EADDR_LEN ; Caller wants a reasonable length?
- jb get_addr_x ; No, fail.
- mov cx, EADDR_LEN ; Move one ethernet address from our copy
- mov si, offset curr_hw_addr ; Copy from most recent setting
- rep movsb
- mov cx, EADDR_LEN ; Tell caller how many bytes we fed him
- clc ; Carry off says success
- ret
- get_addr_x:
- stc ; Tell caller our addr is too big for him
- ret
-
- ;
- ;get the board data. This is (16) bytes starting at remote
- ;dma address 0. Put it in a buffer called board_data.
-
- get_board_data:
- mov cx,10h ; get 16 bytes,
- push ds
- pop es ; set es to ds
- mov di,offset board_data
- mov ax,0 ; from address 0
- call sp_block_input
- ret
-
- public set_address
- set_address:
- assume ds:nothing
- ;enter with ds:si -> Ethernet address, CX = length of address.
- ;exit with nc if okay, or cy, dh=error if any errors.
- ;
- cmp cx,EADDR_LEN ;ensure that their address is okay.
- je set_address_4
- mov dh,BAD_ADDRESS
- stc
- jmp short set_address_done
- set_address_4:
- push cs ; Copy from them to our RAM copy
- pop es ; Destination of move
- mov di, offset curr_hw_addr
- rep movsb ; Move their address
- call set_8390_eaddr ; Put that address in the chip
- set_address_okay:
- mov cx,EADDR_LEN ;return their address length.
- clc
- set_address_done:
- push cs
- pop ds
- assume ds:code
- ret
-
- ; Copy our Ethernet address from curr_hw_addr into the DS8390
- set_8390_eaddr:
- push cs ; Get it from our local RAM copy
- pop ds
- mov si, offset curr_hw_addr
- mov cx, EADDR_LEN ; Move one ethernet address from our copy
- loadport
- setport EN_CCMD ; Chip command register
- pause_
- cli ; Protect from irq changing page bits
- mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
- out dx, al ; Switch to page one for writing eaddr
- setport EN1_PHYS ; Where it goes in 8390
- pause_
- set_8390_1:
- lodsb
- out dx,al
- inc dx
- loop set_8390_1
- loadport
- setport EN_CCMD ; Chip command register
- pause_
- mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
- out dx, al ; Restore to page zero
- sti ; OK for interrupts now
- ret
-
- ; Routines to set address filtering modes in the DS8390
- rcv_mode_1: ; Turn off receiver
- mov al, ENRXCR_MON ; Set to monitor for counts but accept none
- jmp short rcv_mode_set
- rcv_mode_2: ; Receive only packets to this interface
- mov al, 0 ; Set for only our packets
- jmp short rcv_mode_set
- rcv_mode_3: ; Mode 2 plus broadcast packets (This is the default)
- mov al, ENRXCR_BCST ; Set four ours plus broadcasts
- jmp short rcv_mode_set
- rcv_mode_4: ; Mode 3 plus selected multicast packets
- mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
- mov mcast_all_flag,0 ; need to do sw filter.
- mov mcast_sw_filter,1 ; because chip filter is not 100%
- jmp short rcv_mode_set
- rcv_mode_5: ; Mode 3 plus ALL multicast packets
- mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
- mov mcast_all_flag,1
- jmp short rcv_mode_set
- rcv_mode_6: ; Receive all packets (Promiscuous physical plus all multi)
- mov al, ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
- mov mcast_all_flag,1
- rcv_mode_set:
- push ax ; Hold mode until masks are right
- call set_8390_multi ; Set the multicast mask bits in chip
- pop ax
- loadport
- setport EN0_RXCR ; Set receiver to selected mode
- pause_
- out dx, al
- mov rxcr_bits,al ; Save a copy of what we set it to
- ret
-
-
- public set_multicast_list
- set_multicast_list:
- ;enter with ds:si ->list of multicast addresses, cx = number of addresses.
- ;return nc if we set all of them, or cy,dh=error if we didn't.
- assume ds:nothing
- push cs
- pop es ; set es to destination
- mov di,offset mcast_tab
- mov ax,cx ; save byte count
- repz movsb
- mov dx,0
- mov cx,6
- div cx
- mov mcast_hcount,ax
- ;
- mov word ptr mcast_list_bits,0
- mov word ptr mcast_list_bits+2,0
- mov word ptr mcast_list_bits+4,0
- mov word ptr mcast_list_bits+6,0
- ;
- mov cx,mcast_hcount
- inc cx
- mov di,offset mcast_tab_b
- set_mcl_1:
- call add_mc_bits
- add di,6
- loop set_mcl_1
- call set_8390_multi ; Set the multicast mask bits in chip
- clc
- mov dh,0
- ret
-
- ;
- ; multicast is at es:di
- assume ds:nothing
- add_mc_bits:
- push cx
- push di
- mov cx,6
- mov dx,0ffffh ; this is msw.
- mov bx,0ffffh ; set 32 bit number
- add_mcb_1:
- mov al,es:[di]
- inc di
- call upd_crc ; update crc
- loop add_mcb_1 ; and loop.
- mov ah,0
- mov al,dh ; get ms 8 bits,
- rol al,1
- rol al,1
- rol al,1 ; put 3 bits at bottom
- and al,7
- mov dl,al ; save in dl
- mov al,dh ; get ms 8 bits,
- ror al,1
- ror al,1 ; but at bottom
- and al,7
- mov cl,al ; save in cl
- mov al,1
- rol al,cl ; set the correct bit,
- mov di,offset mcast_list_bits
- mov dh,0
- add di,dx
- or cs:[di],al
- pop di
- pop cx
- ret
-
- ;
- ; dx is high,
- ; bx is low.
- ; al is data
-
- upd_crc:
- push cx
- mov cx,8 ; do 8 bits
- mov ah,0
- upd_crc1:
- shl bx,1 ; shift bx
- rcl dx,1 ; through dx
- rcl ah,1 ; carry is at bottom of ah
- xor ah,al ; xor with lsb of data
- rcr ah,1 ; and put in carry bit
- jnc upd_crc2
- ;
- ; autodin is x^32+x^26+x^23x^22+x^16+
- ; x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+1
-
- xor dx,0000010011000001b
- xor bx,0001110110110111b
- upd_crc2:
- shr al,1 ; shift the data
- loop upd_crc1
- pop cx
- ret
-
- ; Set the multicast filter mask bits in case promiscuous rcv wanted
- set_8390_multi:
- push cs
- pop ds
- assume ds:code
- loadport
- setport EN_CCMD ; Chip command register
- pause_
- mov cx, 8 ; Eight bytes of multicast filter
- mov si, offset mcast_list_bits ; Where bits are, if not all ones
- cli ; Protect from irq changing page bits
- mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
- out dx, al ; Switch to page one for writing eaddr
- setport EN1_MULT ; Where it goes in 8390
- pause_
- mov al, mcast_all_flag ; Want all ones or just selected bits?
- or al, al
- je set_mcast_2 ; Just selected ones
- mov al, 0ffh ; Ones for filter
- set_mcast_all:
- out dx, al ; Write a mask byte
- inc dl ; Step to next one
- loop set_mcast_all ; ..
- jmp short set_mcast_x
-
- set_mcast_2:
- lodsb ; Get a byte of mask bits
- out dx, al ; Write a mask byte
- inc dl ; Step to next I/O register
- loop set_mcast_2 ; ..
- set_mcast_x:
- loadport
- setport EN_CCMD ; Chip command register
- pause_
- mov al, ENC_NODMA+ENC_PAGE0+ENC_START
- out dx, al ; Restore to page zero
- sti ; OK for interrupts now
- ret
-
-
- public reset_chip
-
- reset_chip:
- assume ds:nothing
- loadport ; Base of I/O regs
- setport NE_OTHERPORT
- in al,dx
- longpause
- out dx,al ; should set command 21, 80
- longpause
- setport EN_CCMD ; Chip command reg
- pause_
- mov al, ENC_STOP+ENC_NODMA
- out dx, al ; Stop the DS8390
- setport EN0_ISR
- pause_
- mov cx,0
- reset_chip_loop:
- in al,dx ; get isr
- and al,ENISR_RESET
- jnz reset_chip_done
- jmp reset_chip_loop
- reset_chip_done:
- ret
-
- public terminate
- terminate:
- ret
-
- public reset_interface
- reset_interface:
- assume ds:code
- call reset_chip
- loadport ; Base of I/O regs
- setport EN0_ISR ; Interrupt status reg
- pause_
- mov al, 0ffh ; Clear all pending interrupts
- out dx, al ; ..
- setport EN0_IMR ; Interrupt mask reg
- pause_
- xor al, al ; Turn off all enables
- out dx, al ; ..
- ret
-
- ;
- ; Special case Block input routine. Used on extra memory
- ; space for board ID etc. DMA count is set X2,
- ; CX = byte count, es:si = buffer location, ax = buffer address
-
- sp_block_input:
- push ax ; save buffer address
- loadport
- setport EN_CCMD
- pause_
- mov al,ENC_NODMA+ENC_STOP
- out dx,al ; stop & clear the chip
- setport EN0_RCNTLO ; remote byte count 0
- pause_
- mov ax,cx
- add ax,ax
- out dx,al
- setport EN0_RCNTHI
- pause_
- mov al,ah
- out dx,al
- pop ax ; get our page back
- setport EN0_RSARLO
- pause_
- out dx,al ; set as hi address
- setport EN0_RSARHI
- pause_
- mov al,ah
- out dx,al
- setport EN_CCMD
- pause_
- mov al,ENC_RREAD+ENC_START ; read and start
- out dx,al
- setport NE_DATAPORT
- pause_
- jmp read_loop
- ;
- ; Block input routine
- ; CX = byte count, es:si = buffer location, ax = buffer address
-
- public block_input
- block_input:
- push ax ; save buffer address
- loadport
- setport EN_CCMD
- pause_
- mov al,ENC_NODMA+ENC_PAGE0+ENC_START
- out dx,al
- setport EN0_RCNTLO ; remote byte count 0
- pause_
- mov al,cl
- out dx,al
- setport EN0_RCNTHI
- pause_
- mov al,ch
- out dx,al
- pop ax ; get our page back
- setport EN0_RSARLO
- pause_
- out dx,al ; set as hi address
- setport EN0_RSARHI
- pause_
- mov al,ah
- out dx,al
- setport EN_CCMD
- pause_
- mov al,ENC_RREAD+ENC_START ; read and start
- out dx,al
- setport NE_DATAPORT
- pause_
- cmp byte ptr is_186,0
- jnz read_186
- read_loop:
- in al,dx ; get a byte
- stosb ; save it
- loop read_loop
- ret
- read_186:
- inc cx ; make even
- shr cx,1 ; word count
- db 0f3h, 06dh ;masm 4.0 doesn't grok "rep insw"
- ret
- ;
- ; Block output routine
- ; CX = byte count, ds:si = buffer location, ax = buffer address
-
- block_output:
- assume ds:nothing
- push ax ; save buffer address
- inc cx ; make even
- and cx,0fffeh
- loadport
- setport EN_CCMD
- pause_
- mov al,ENC_NODMA+ENC_START
- out dx,al ; stop & clear the chip
- setport EN0_RCNTLO ; remote byte count 0
- pause_
- mov al,cl
- out dx,al
- setport EN0_RCNTHI
- pause_
- mov al,ch
- out dx,al
- pop ax ; get our page back
- setport EN0_RSARLO
- pause_
- out dx,al ; set as lo address
- setport EN0_RSARHI
- pause_
- mov al,ah
- out dx,al
- setport EN_CCMD
- pause_
- mov al,ENC_RWRITE+ENC_START ; write and start
- out dx,al
- setport NE_DATAPORT
- pause_
- cmp byte ptr is_186,0
- jnz write_186
- write_loop:
- lodsb ; get a byte
- out dx,al ; save it
- loop write_loop
- ret
- write_186:
- shr cx,1 ; word count
- db 0f3h, 06fh ;masm 4.0 doesn't grok "rep outsw"
- ret
-
-
-
- ; Linkages to non-device-specific routines
- ;called when we want to determine what to do with a received packet.
- ;enter with cx = packet length, es:di -> packet type.
- ;It returns with es:di = 0 if don't want this type or if no buffer available.
- extrn recv_find: near
-
- ;called after we have copied the packet into the buffer.
- ;enter with ds:si ->the packet, cx = length of the packet.
- extrn recv_copy: near
-
- extrn count_in_err: near
- extrn count_out_err: near
-
- public recv
- recv:
- ;called from the recv isr. All registers have been saved, and ds=cs.
- ;Actually, not just receive, but all interrupts come here.
- ;Upon exit, the interrupt will be acknowledged.
-
- assume ds:code
- check_isr: ; Was there an interrupt from this card?
-
- loadport ; Point at card's I/O port base
- setport EN0_IMR ; point at interrupt masks
- pause_ ; switch off, this way we can
- mov al,0 ; leave the chip running.
- out dx,al ; no interrupts please.
- setport EN0_ISR ; Point at interrupt status register
- pause_
- in al, dx ; Get pending interrupts
- and al, ENISR_ALL ; Any?
- jnz isr_test_overrun
- jmp interrupt_done ; Go if none
- ; First, a messy procedure for handling the case where the rcvr
- ; over-runs its ring buffer. This is spec'ed by National for the chip.
- ; This is handled differently in sample code from 3Com and from WD.
- ; This is close to the WD version. May need tweaking if it doesn't
- ; work for the 3Com card.
-
- isr_test_overrun:
- test al,ENISR_OVER ; Was there an overrun?
- jnz recv_overrun ; Go if so.
- jmp recv_no_overrun ; Go if not.
- recv_overrun:
- setport EN_CCMD ; Stop the chip
- pause_
- mov al, ENC_STOP+ENC_NODMA
- out dx, al ; Write "stop" to command register
-
-
- mov al, ENC_NODMA+ENC_PAGE1 ; Could be in previous out, but
- out dx,al ; was only tested this way
- setport EN1_CURPAG ; Get current page
- in al,dx
- mov bl,al ; save it
- setport EN_CCMD ;
- mov al, ENC_NODMA+ENC_PAGE0
- out dx,al ; Back to page 0
-
- ; Remove one frame from the ring
- setport EN0_BOUNDARY ; Find end of this frame
- pause_
- in al, dx ; Get memory page number
- inc al ; Page plus 1
- cmp al, SM_RSTOP_PG ; Wrapped around ring?
- jnz rcv_ovr_nwrap ; Go if not
- mov al, SM_RSTART_PG ; Yes, wrap the page pointer
- rcv_ovr_nwrap:
-
- cmp al,bl ; Check if buffer emptry
- je rcv_ovr_empty ; Yes ? Don't receive anything
-
- mov ah,al ; make a byte address. e.g. page
- mov bl,ah ; and save in bl
- mov al,0 ; 46h becomes 4600h into buffer
- mov cx,RCV_HDR_SIZE ; size of rcv_hdr
- mov di,offset rcv_hdr ;point to header
- push ds
- pop es ; set es to right place
- call block_input
- mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
- test al,ENPS_RXOK ; Is this frame any good?
- jz rcv_ovr_ng ; Skip if not
- call rcv_frm ; Yes, go accept it
- rcv_ovr_ng:
- mov al, rcv_hdr+EN_RBUF_NXT_PG ; Get pointer to next frame
- dec al ; Back up one page
- cmp al, SM_RSTART_PG ; Did it wrap?
- jge rcv_ovr_nwr2
- mov al, SM_RSTOP_PG-1 ; Yes, back to end of ring
- rcv_ovr_nwr2:
- loadport ; Point at boundary reg
- setport EN0_BOUNDARY ; ..
- pause_
- out dx, al ; Set the boundary
- rcv_ovr_empty:
- setport EN0_RCNTLO ; Point at byte count regs
- pause_
- xor al, al ; Clear them
- out dx, al ; ..
- setport EN0_RCNTHI
- pause_
- out dx, al
- setport EN0_ISR ; Point at status reg
- pause_
- mov cx, 8000h ; Timeout counter
- rcv_ovr_rst_loop:
- in al, dx ; Is it finished resetting?
- test al,ENISR_RESET ; ..
- jnz rcv_ovr_rst ; Go if so
- dec cx ; Loop til reset, or til timeout
- jnz rcv_ovr_rst_loop
- rcv_ovr_rst:
- loadport ; Point at Transmit control reg
- setport EN0_TXCR ; ..
- pause_
- mov al, ENTXCR_LOOP ; Put transmitter in loopback mode
- out dx, al ; ..
- setport EN_CCMD ; Point at Chip command reg
- pause_
- mov al, ENC_START+ENC_NODMA
- out dx, al ; Start the chip running again
- setport EN0_TXCR ; Back to TX control reg
- pause_
- xor al, al ; Clear the loopback bit
- out dx, al ; ..
- setport EN0_ISR ; Point at Interrupt status register
- pause_
- mov al, ENISR_OVER ; Clear the overrun interrupt bit
- out dx, al ; ..
- call count_in_err ; Count the anomaly
- jmp check_isr ; Done with the overrun case
-
- recv_no_overrun:
- ; Handle receive flags, normal and with error (but not overrun).
- test al,ENISR_RX+ENISR_RX_ERR ; Frame received without overrun?
- jnz recv_frame ; Go if so.
- jmp recv_no_frame ; Go if not.
- recv_frame:
- loadport ; Point at Chip's Command Reg
- setport EN_CCMD ; ..
- pause_
- mov al, ENC_NODMA+ENC_PAGE1+ENC_START
- out dx, al ; Switch to page 1 registers
- setport EN1_CURPAG ;Get current page of rcv ring
- pause_
- in al, dx ; ..
- mov ah, al ; Hold current page in AH
- setport EN_CCMD ; Back to page zero registers
- pause_
- mov al, ENC_NODMA+ENC_PAGE0+ENC_START
- out dx, al ; Switch back to page 0 registers
- setport EN0_BOUNDARY ;Get boundary page
- pause_
- in al, dx ; ..
- inc al ; Step boundary from last used page
- cmp al, SM_RSTOP_PG ; Wrap if needed
- jne rx_nwrap3 ; Go if not
- mov al, SM_RSTART_PG ; Wrap to first RX page
- rx_nwrap3:
- cmp al, ah ; Read all the frames?
- je recv_frame_break ; Finished them all
-
- mov ah,al ; make a byte address. E.G. page
- mov al,0 ; 46h becomes 4600h into buffer
- mov bl,ah
- mov cx,RCV_HDR_SIZE
- mov di,offset rcv_hdr
- push ds
- pop es ; set es to right place
- call block_input
- mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
- test al,ENPS_RXOK ; Good frame?
- jz recv_err_no_rcv
- call rcv_frm ; Yes, go accept it
- jmp recv_no_rcv
- recv_err_no_rcv:
- or byte ptr soft_rx_err_bits,al
- add word ptr soft_rx_errors,1
- adc word ptr soft_rx_errors+2,0
- recv_no_rcv:
- mov al, rcv_hdr+EN_RBUF_NXT_PG ; Start of next frame
- dec al ; Make previous page for new boundary
- cmp al, SM_RSTART_PG ; Wrap around the bottom?
- jge rcv_nwrap4
- mov al, SM_RSTOP_PG-1 ; Yes
- rcv_nwrap4:
- loadport ; Point at the Boundary Reg again
- setport EN0_BOUNDARY ; ..
- pause_
- out dx, al ; Set new boundary
- jmp recv_frame ; See if any more frames
-
- recv_frame_break:
- loadport ; Point at Interrupt Status Reg
- setport EN0_ISR ; ..
- pause_
- mov al, ENISR_RX+ENISR_RX_ERR+ENISR_OVER
- out dx, al ; Clear those requests
- setport EN_CCMD
- pause_
- mov al, ENC_NODMA+ENC_PAGE0+ENC_START
- out dx,al
- jmp check_isr ; See if any other interrupts pending
-
- recv_no_frame: ; Handle transmit flags.
- test al,ENISR_TX+ENISR_TX_ERR ; Frame transmitted?
- jnz isr_tx ; Go if so.
- jmp isr_no_tx ; Go if not.
- isr_tx:
- mov ah, al ; Hold interrupt status bits
- loadport ; Point at Transmit Status Reg
- setport EN0_TSR ; ..
- pause_
- in al, dx ; ..
- test ah,ENISR_TX ; Non-error TX?
- jz isr_tx_err ; No, do TX error completion
- call count_soft_err ; soft error ??
- test al,ENTSR_COLL16 ; Jammed for 16 transmit tries?
- jz isr_tx_njam ; Go if not
- call count_out_err ; Yes, count those
- isr_tx_njam:
- setport EN0_ISR ; Clear the TX complete flag
- pause_
- mov al, ENISR_TX ; ..
- out dx, al ; ..
- jmp isr_tx_done
- isr_tx_err:
- test al,ENTSR_FU ; FIFO Underrun?
- jz isr_txerr_nfu
- call count_out_err ; Yes, count those
- isr_txerr_nfu:
- loadport ; Clear the TX error completion flag
- setport EN0_ISR ; ..
- pause_
- mov al, ENISR_TX_ERR ; ..
- out dx, al ; ..
- isr_tx_done:
- ; If TX queue and/or TX shared memory ring buffer were being
- ; used, logic to step through them would go here. However,
- ; in this version, we just clear the flags for background to notice.
-
- jmp check_isr ; See if any other interrupts on
-
- isr_no_tx:
- ; Now check to see if any counters are getting full
- test al,ENISR_COUNTERS ; Interrupt to handle counters?
- jnz isr_stat ; Go if so.
- jmp isr_no_stat ; Go if not.
- isr_stat:
- ; We have to read the counters to clear them and to clear the interrupt.
- ; Version 1 of the PC/FTP driver spec doesn't give us
- ; anything useful to do with the data, though.
- ; Fix this up for V2 one of these days.
- loadport ; Point at first counter
- setport EN0_COUNTER0 ; ..
- pause_
- in al, dx ; Read the count, ignore it.
- setport EN0_COUNTER1
- pause_
- in al, dx ; Read the count, ignore it.
- setport EN0_COUNTER2
- pause_
- in al, dx ; Read the count, ignore it.
- setport EN0_ISR ; Clear the statistics completion flag
- pause_
- mov al, ENISR_COUNTERS ; ..
- out dx, al ; ..
- isr_no_stat:
- jmp check_isr ; Anything else to do?
-
- interrupt_done:
- ret
-
- ; Do the work of copying out a receive frame.
- ; Called with bl/ the page number of the frame header in shared memory
-
- public rcv_frm
- rcv_frm:
- ; first do a software multicast filter.
- push bx ; save page.
- cmp mcast_sw_filter,1 ; do software check of mcast ?
- jnz rcv_frm_ok ; no, accept.
- mov ax,word ptr rcv_hdr+EN_RBUF_NHDR ; get first word of address
- test al,1 ; odd first byte
- jz rcv_frm_ok ; must be our address if even
- inc word ptr mcast_sw_fin
-
- mov bx,word ptr rcv_hdr+EN_RBUF_NHDR+2 ; get second word of address
- mov dx,word ptr rcv_hdr+EN_RBUF_NHDR+4 ; get third word of address
-
- mov di,offset mcast_tab_b ; point to table and broadcast
- mov cx,mcast_hcount ; get number in table
- inc cx ; plus the broadcast
- rcv_loop:
- mov si,di ; save this table entry
- cmp ax,[di]
- jnz rcv_trynext
- inc di
- inc di
- cmp bx,[di]
- jnz rcv_trynext
- inc di
- inc di
- cmp dx,[di]
- jz rcv_mc_ok ; got it.
- rcv_trynext:
- mov di,si ; get table back,
- add di,6
- loop rcv_loop
- pop bx ; restore bx
- jmp rcv_no_copy
-
- rcv_mc_ok:
- inc word ptr mcast_sw_fout
- rcv_frm_ok:
- ; Set cx to length of this frame.
- mov ch, rcv_hdr+EN_RBUF_SIZE_HI ; Extract size of frame
- mov cl, rcv_hdr+EN_RBUF_SIZE_LO ; Extract size of frame
- sub cx, EN_RBUF_NHDR ; Less the header stuff
- ; Set es:di to point to Ethernet type field.
- mov di, offset rcv_hdr+EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
- push cx ; Save frame size
- push es
- mov ax, cs ; Set ds = code
- mov ds, ax
- mov es,ax
- assume ds:code
- call recv_find ; See if type and size are wanted
- pop ds ; RX page pointer in ds now
- assume ds:nothing
- pop cx
- pop bx
- cld ; Copies below are forward, please
- mov ax, es ; Did recv_find give us a null pointer?
- or ax, di ; ..
- je rcv_no_copy ; If null, don't copy the data
-
- push cx ; We will want the count and pointer
- push es ; to hand to client after copying,
- push di ; so save them at this point
- mov ah,bl ; set ax to page to start from
- mov al,EN_RBUF_NHDR ; skip the header stuff
- call block_input
- pop si ; Recover pointer to destination
- pop ds ; Tell client it's his source
- pop cx ; And it's this long
- assume ds:nothing
- call recv_copy ; Give it to him
- rcv_no_copy:
- push cs ; Put ds back in code space
- pop ds ; ..
- assume ds:code
- ret ; That's it for rcv_frm
-
-
- public recv_exiting
- recv_exiting:
- ;called from the recv isr after interrupts have been acknowledged.
- ;Only ds and ax have been saved.
- assume ds:nothing
- push dx
- loadport
- setport EN0_IMR ; Tell card it can cause these interrupts
- pause_
- mov al, ENISR_ALL
- out dx, al
- pop dx
- ret
-
-
- ;any code after this will not be kept after initialization.
- end_resident label byte
-
-
- public usage_msg
- usage_msg db "usage: NE2000 [-n] [-d] [-w] <packet_int_no> <int_level> <io_addr>",CR,LF,'$'
-
- public copyright_msg
- copyright_msg db "Packet driver for Novell NE2000, version ",'0'+majver,".",'0'+version,CR,LF
- db "Portions Copyright 1989, Robert C. Clements, K1BC",CR,LF,'$'
-
- cfg_err_msg:
- db "NE2000 Configuration failed. Check parameters.",CR,LF,'$'
- int_no_name:
- db "Interrupt number ",'$'
- io_addr_name:
- db "I/O port ",'$'
- using_186_msg db "Using 80[123]86 I/O instructions.",CR,LF,'$'
-
- extrn set_recv_isr: near
-
- ;enter with si -> argument string, di -> word to store.
- ;if there is no number, don't change the number.
- extrn get_number: near
-
- ;enter with dx -> name of word, di -> dword to print.
- extrn print_number: near
-
- public parse_args
- parse_args:
- ;exit with nc if all went well, cy otherwise.
- mov di, offset int_no ; May override interrupt channel
- call get_number
- mov di, offset io_addr ; May override I/O address
- call get_number
- ; mov di, offset mem_base ; Not movable in 3C503
- ; call get_number ; Must get from jumpers.
- clc
- ret
-
-
- cfg_error:
- mov dx,offset cfg_err_msg
- error:
- mov ah,9 ; Type the msg
- int 21h
- stc ; Indicate error
- ret ; Return to common code
-
- ; Called once to initialize the NE2000 card
-
- public etopen
- etopen: ; Initialize interface
-
- ;Determine the processor type. The 8088 and 8086 will actually shift ax
- ;over by 33 bits, while the 80[123]86 use a shift count mod 32.
- ;This bit lifted from NI5010 driver.
-
- mov cl,33
- mov ax,0ffffh
- shl ax,cl
- jz not_186
- mov is_186,1
- mov dx,offset using_186_msg
- mov ah,9
- int 21h
- not_186:
-
- ; Now, initialize the DS8390 Ethernet Controller chip
- ini_8390:
- call reset_chip
- loadport
- setport EN0_DCFG ; Configure the fifo organization
- pause_
- mov al, ENDCFG_BM8 ; Fifo threshold = 8 bytes
- out dx, al
- setport EN_CCMD ; DS8390 chip's command register
- pause_
- mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
- out dx, al ; Switch to page zero
-
- setport EN0_TXCR ; Set transmitter mode to normal
- pause_
- xor al, al
- out dx, al
- setport EN0_RXCR ; Set receiver to monitor mode
- pause_
- mov al, ENRXCR_MON
- out dx, al
-
-
- ; Set up control of shared memory, buffer ring, etc.
-
- setport EN0_STARTPG ; Set receiver's first buffer page
- pause_
- mov al, SM_RSTART_PG
- out dx, al
-
- setport EN0_STOPPG ; and receiver's last buffer page + 1
- pause_
- mov al, SM_RSTOP_PG
- out dx, al
-
- setport EN0_BOUNDARY ; Set initial "last page we have emptied"
- pause_
- mov al, SM_RSTOP_PG ; (WD doc says set to RSTART_PG)
- dec al ; (3Com doc says set to RSTOP_PG-1 ?)
- ; ; (and 3Com handling of BOUNDARY is
- ; ; different throughout.)
- out dx, al ; (Check out why WD and 3Com disagree)
- ;
-
- setport EN0_IMR ; Clear all interrupt enable flags
- pause_
- xor al, al
- out dx, al
-
- setport EN0_ISR ; Clear all interrupt assertion flags
- pause_
- mov al, 0ffh
- out dx, al
-
- setport EN_CCMD
- pause_
- mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
- out dx,al
-
- setport EN1_CURPAG
- pause_
- mov al, SM_RSTART_PG
- out dx, al
-
- setport EN_CCMD
- pause_
- mov al, ENC_NODMA+ENC_PAGE0+ENC_START
- out dx,al
-
- call get_board_data ; read board data
-
- push ds ; Copy from card's address to current address
- pop es
-
- mov si, offset board_data ; address is at start
- mov di, offset curr_hw_addr
- mov cx, EADDR_LEN ; Copy one address length
- rep movsb ; ..
- call set_8390_eaddr ; Now set the address in the 8390 chip
- call set_8390_multi ; Put the right stuff into 8390's multicast masks
- loadport ; Base of I/O regs
- setport EN_CCMD ; Chip command register
- pause_
- mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
- out dx, al ; Back to page zero
- setport EN0_RCNTLO ; Clear the byte count registers
- pause_
- xor al, al ; ..
- out dx, al
- setport EN0_RCNTHI
- pause_
- out dx, al ; Clear high byte, too
- setport EN0_IMR ; Clear all interrupt enable flags
- pause_
- xor al, al
- out dx, al
- setport EN0_ISR ; Clear all interrupt assertion flags
- pause_
- mov al, 0ffh ; again for safety before making the
- out dx, al ; interrupt be enabled
- call set_recv_isr ; Put ourselves in interrupt chain
- loadport
- setport EN_CCMD ; Now start the DS8390
- pause_
- mov al, ENC_START+ENC_NODMA
- out dx, al ; interrupt be enabled
- setport EN0_RXCR ; Tell it what frames to accept
- pause_
- mov al, rxcr_bits ; As most recently set by set_mode
- out dx, al
- setport EN0_IMR ; Tell card it can cause these interrupts
- pause_
- mov al, ENISR_ALL
- out dx, al
-
- mov al, int_no ; Get board's interrupt vector
- add al, 8
- cmp al, 8+8 ; Is it a slave 8259 interrupt?
- jb set_int_num ; No.
- add al, 70h - 8 - 8 ; Map it to the real interrupt.
- set_int_num:
- xor ah, ah ; Clear high byte
- mov int_num, ax ; Set parameter_list int num.
-
- mov dx, offset end_resident ; Report our size
- clc ; Say no error
- ret ; Back to common code
-
- public print_parameters
- print_parameters:
- ;echo our command-line parameters
- mov di,offset int_no ; May override interrupt channel
- mov dx,offset int_no_name ; Message for it
- call print_number
- mov di,offset io_addr ; May override I/O address
- mov dx,offset io_addr_name ; Message for it
- call print_number
- ret
-
- code ends
-
- end
-